package com.tiancheng.trade.payment.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tiancheng.trade.commom.core.data.notify.PayRefundSettleResultMsgBO;
import com.tiancheng.trade.commom.core.data.notify.PaySettleResultMsgBO;
import com.tiancheng.trade.commom.core.enums.SettleRuleTypeEnums;
import com.tiancheng.trade.commom.core.enums.SettleStatusEnum;
import com.tiancheng.trade.commom.core.enums.VerificationStatusEnum;
import com.tiancheng.trade.commom.core.exception.Asserts;
import com.tiancheng.trade.commom.core.utils.IdWorkerUtils;
import com.tiancheng.trade.payment.bo.SettleFlowDetailMerchantBO;
import com.tiancheng.trade.payment.channel.bo.UnifiedOrderSettleBO;
import com.tiancheng.trade.payment.convert.SettleConvert;
import com.tiancheng.trade.payment.enums.WithdrawalStatusEnum;
import com.tiancheng.trade.payment.mapper.PaymentFlowDetailSettleMapper;
import com.tiancheng.trade.payment.model.*;
import com.tiancheng.trade.payment.mq.PaymentCenterMsgProducer;
import com.tiancheng.trade.payment.service.IPaymentFlowDetailSettleDetailService;
import com.tiancheng.trade.payment.service.IRefundSettleDetailService;
import com.tiancheng.trade.payment.service.IRefundSettleService;
import com.tiancheng.trade.payment.service.PaymentFlowDetailSettleService;
import com.tiancheng.trade.payment.util.helper.PaymentIdHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName PaymentFlowDetailSettleServiceImpl
 * @Description 支付明细结算信息 service impl
 * @Author kellen
 * @Date 2024/11/28 21:52
 */
@Slf4j
@Service
public class PaymentFlowDetailSettleServiceImpl extends ServiceImpl<PaymentFlowDetailSettleMapper, PaymentFlowDetailSettle> implements PaymentFlowDetailSettleService {

    @Resource
    private IPaymentFlowDetailSettleDetailService settleDetailService;
    @Resource
    private IRefundSettleDetailService refundSettleDetailService;
    @Resource
    private IRefundSettleService refundSettleService;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private PaymentCenterMsgProducer paymentCenterMsgProducer;

    @Override
    public List<PaymentFlowDetailSettle> queryBySubFlowIds(String flowId, Set<String> subFlowIds) {
        return list(new LambdaQueryWrapper<PaymentFlowDetailSettle>().eq(PaymentFlowDetailSettle::getFlowId, flowId).in(PaymentFlowDetailSettle::getSubFlowId, subFlowIds));
    }

    @Override
    public boolean genSettle(PaymentOrder flow, List<PaymentFlowDetail> flowDetails, List<UnifiedOrderSettleBO> subSettles) {
        Asserts.isNotNull(flow, "生成结算信息异常，支付流水为空！");
        Asserts.isNotEmpty(flowDetails, "生成结算信息异常，支付流水明细为空！");
        // 结算子订单-汇总项
        List<PaymentFlowDetailSettle> detailSettles = new ArrayList<>();
        // 结算子订单-明细项
        List<PaymentFlowDetailSettleDetail> detailSettlesDetails = new ArrayList<>();
        // 结算批次号
        final String settleBatchNo = PaymentIdHelper.getSettleBatchNo(flow.getOrgPaymentChannelId());
        for (PaymentFlowDetail flowDetail : flowDetails) {
            // 子订单结算明细汇总
            Optional<UnifiedOrderSettleBO> unifiedOrderSettleBOOptional = subSettles.stream().filter(ss -> ss.getSubFlowId().equals(flowDetail.getSubFlowId())).findFirst();
            Asserts.isTrue(unifiedOrderSettleBOOptional.isPresent(), "生成结算信息异常，订单结算明细异常！");
            UnifiedOrderSettleBO unifiedOrderSettleBO = unifiedOrderSettleBOOptional.get();

            final PaymentFlowDetailSettle flowDetailSettle = new PaymentFlowDetailSettle();
            flowDetailSettle.setId(IdWorkerUtils.getInstance().nextId());
            flowDetailSettle.setOrgPaymentChannelId(flow.getOrgPaymentChannelId());
            flowDetailSettle.setFlowId(flowDetail.getFlowId());
            flowDetailSettle.setSubFlowId(flowDetail.getSubFlowId());
            flowDetailSettle.setMerchantCode(flowDetail.getMerchantCode());
            flowDetailSettle.setSubMerchantId(unifiedOrderSettleBO.getSettleItems().stream().filter(i -> i.getMerchantCode().equals(flowDetail.getMerchantCode())).findFirst().get().getMid());
            flowDetailSettle.setSettleRuleType(SettleRuleTypeEnums.getByRuleCode(flowDetail.getSettleRuleCode()).get().getCode());
            flowDetailSettle.setSettleRuleCode(flowDetail.getSettleRuleCode());
            flowDetailSettle.setSubOrderAmount(flowDetail.getOrderAmount());
            flowDetailSettle.setSettleStatus(SettleStatusEnum.WAIT.getCode());
            flowDetailSettle.setVerificationStatus(VerificationStatusEnum.WAIT.getCode());
            flowDetailSettle.setCreateTime(LocalDateTime.now());
            //flowDetailSettle.setSettleDate(LocalDate.now());
            flowDetailSettle.setSettleFlowId(unifiedOrderSettleBOOptional.get().getSettleFlowId());
            flowDetailSettle.setBatchNo(settleBatchNo);
            flowDetailSettle.setPaymentChannel(flow.getPaymentChannel());

            // 平台佣金计算
            final int platformAmount = unifiedOrderSettleBO.getPlatformAmount();
            // 平台佣金
            flowDetailSettle.setPlatformAmount(platformAmount);

            // 商户分账金额
            int merchantAmount = 0;
            int subFlowInc = 1; // 子订单流水起始值
            for (UnifiedOrderSettleBO.UnifiedOrderSettleItemBO settleItem : unifiedOrderSettleBO.getSettleItems()) {
                final PaymentFlowDetailSettleDetail merchantDetail = new PaymentFlowDetailSettleDetail();
                merchantDetail.setSubMerchantId(flowDetailSettle.getSubFlowId());
                merchantDetail.setSettleMerchantId(settleItem.getEnterpriseCode());
                merchantDetail.setSubMerchantType(settleItem.getIsPlatformMerchant() ? 0 : (settleItem.getMerchantCode().equals(flowDetail.getMerchantCode()) ? 1 : 2));
                merchantDetail.setMerchantRegId(settleItem.getMerchantRegId());
                merchantDetail.setMerchantCode(settleItem.getMerchantCode());
                merchantDetail.setMerchantName(settleItem.getMerchantName());
                merchantDetail.setSubMerchantId(settleItem.getMid());
                merchantDetail.setAmount(settleItem.getTotalAmount());
                merchantDetail.setSettleFlowId(flowDetailSettle.getSettleFlowId());
                merchantDetail.setSubSettleFlowId(PaymentIdHelper.getSubOrderFlowId(flowDetailSettle.getSettleFlowId(), subFlowInc));
                merchantDetail.setSubFlowId(flowDetail.getSubFlowId());
                merchantDetail.setSettleId(flowDetailSettle.getId());
                merchantDetail.setWithdrawalId("");
                merchantDetail.setSettleStatus(SettleStatusEnum.WAIT.getCode());
                detailSettlesDetails.add(merchantDetail);
                if (merchantDetail.getSubMerchantType().equals(1) && merchantDetail.getMerchantRegId().equals(settleItem.getMerchantRegId())) {
                    merchantAmount += merchantDetail.getAmount();
                }
                subFlowInc++;
            }
            flowDetailSettle.setMerchantAmount(merchantAmount);
            detailSettles.add(flowDetailSettle);
        }
        Boolean isSc = transactionTemplate.execute(transactionStatus -> {
            try {
                this.saveBatch(detailSettles);
                settleDetailService.saveBatch(detailSettlesDetails);
                return true;
            } catch (Exception e) {
                transactionStatus.setRollbackOnly();
                throw e;
            }
        });
        Asserts.isTrue(null != isSc && isSc, "结算信息生成失败！");
        return true;
    }

    @Override
    public List<SettleFlowDetailMerchantBO> distinctSettleMerchant(LocalDate expectSettleDate, String payChannel) {
        return baseMapper.distinctSettleMerchant(expectSettleDate, payChannel);
    }

    @Override
    public IPage<PaymentFlowDetailSettle> listWaitSettles(LocalDate expectSettleDate, String payChannel, long current, long size) {
        Page<PaymentFlowDetailSettle> page = new Page<>(current, size);
        return baseMapper.pageWaitSettles(expectSettleDate, payChannel, new Page<>(current, size));
    }

    @Override
    public boolean withdrawResult(String withdrawalId, WithdrawalStatusEnum withdrawalStatusEnum, LocalDateTime transTime) {
        List<PaymentFlowDetailSettleDetail> settleDetails = settleDetailService.list(new LambdaQueryWrapper<PaymentFlowDetailSettleDetail>()
                .eq(PaymentFlowDetailSettleDetail::getWithdrawalId, withdrawalId).select(PaymentFlowDetailSettleDetail::getId, PaymentFlowDetailSettleDetail::getSettleId, PaymentFlowDetailSettleDetail::getWithdrawalId));


        List<RefundSettleDetail> refundSettleDetails = refundSettleDetailService.list(new LambdaQueryWrapper<RefundSettleDetail>()
                .eq(RefundSettleDetail::getWithdrawalId, withdrawalId).select(RefundSettleDetail::getId, RefundSettleDetail::getSettleId, RefundSettleDetail::getWithdrawalId));
        if (CollectionUtils.isEmpty(settleDetails) && CollectionUtils.isEmpty(refundSettleDetails)) {
            return true;
        }
        PaymentFlowDetailSettleDetail updateDetail = new PaymentFlowDetailSettleDetail();
        RefundSettleDetail updRefundSettleDetail = new RefundSettleDetail();
        if (withdrawalStatusEnum.equals(WithdrawalStatusEnum.FAIL)) {
            // 提现失败了，标记所有流水状态为失败，等待下一个周期继续处理
            updateDetail.setSettleStatus(SettleStatusEnum.FAIL.getCode());
            updRefundSettleDetail.setSettleStatus(SettleStatusEnum.FAIL.getCode());
            settleDetailService.update(updateDetail, new LambdaQueryWrapper<PaymentFlowDetailSettleDetail>().in(PaymentFlowDetailSettleDetail::getId, settleDetails.stream().map(PaymentFlowDetailSettleDetail::getId).collect(Collectors.toSet())));
            if (CollectionUtils.isNotEmpty(refundSettleDetails)) {
                refundSettleDetailService.update(updRefundSettleDetail, new LambdaQueryWrapper<RefundSettleDetail>().in(RefundSettleDetail::getId, settleDetails.stream().map(PaymentFlowDetailSettleDetail::getId).collect(Collectors.toSet())));
            }
            return true;
        }
        // 提现操作成功了
        // 1、更新结算明细项为成功
        updateDetail.setSettleStatus(SettleStatusEnum.SUCCESS.getCode());
        updRefundSettleDetail.setSettleStatus(SettleStatusEnum.SUCCESS.getCode());
        settleDetailService.update(updateDetail, new LambdaQueryWrapper<PaymentFlowDetailSettleDetail>().in(PaymentFlowDetailSettleDetail::getId, settleDetails.stream().map(PaymentFlowDetailSettleDetail::getId).collect(Collectors.toSet())));
        if (CollectionUtils.isNotEmpty(refundSettleDetails)) {
            refundSettleDetailService.update(updRefundSettleDetail, new LambdaQueryWrapper<RefundSettleDetail>()
                    .in(RefundSettleDetail::getId, refundSettleDetails.stream().map(RefundSettleDetail::getId).collect(Collectors.toSet())));
        }
        // 2、判断是否有结算完成的结算单，有的话推送结算结果
        if (CollectionUtils.isNotEmpty(settleDetails)) {
            Set<Long> settleIds = settleDetails.stream().map(PaymentFlowDetailSettleDetail::getSettleId).collect(Collectors.toSet());
            // 已经结算完成的结算单，推送到订单中心
            List<PaymentFlowDetailSettle> completedSettles = this.baseMapper.completedSettles(settleIds);
            if (CollectionUtils.isNotEmpty(completedSettles)) {
                // 更新结算单状态
                PaymentFlowDetailSettle paymentFlowDetailSettle = new PaymentFlowDetailSettle();
                paymentFlowDetailSettle.setSettleStatus(SettleStatusEnum.SUCCESS.getCode());
                paymentFlowDetailSettle.setSettleScDate(null == transTime ? LocalDateTime.now() : transTime);
                this.update(paymentFlowDetailSettle, new LambdaQueryWrapper<PaymentFlowDetailSettle>().in(PaymentFlowDetailSettle::getId, settleIds));
                // 推送给到订单中心
                for (PaymentFlowDetailSettle completedSettle : completedSettles) {
                    PaySettleResultMsgBO paySettleResultMsgBO = SettleConvert.INSTANCE.convert2PaySettleResultMsgBO(completedSettle);
                    paySettleResultMsgBO.setSettleScDate(paymentFlowDetailSettle.getSettleScDate());
                    paySettleResultMsgBO.setSettleStatus(paymentFlowDetailSettle.getSettleStatus());
                    paymentCenterMsgProducer.paySettleResult(paySettleResultMsgBO);
                }
            }
        }

        // 已经结算完成的结算单，推送到订单中心
        if (CollectionUtils.isNotEmpty(refundSettleDetails)) {
            Set<Long> refundSettleIds = refundSettleDetails.stream().map(RefundSettleDetail::getSettleId).collect(Collectors.toSet());
            List<RefundSettle> completedRefundSettles = refundSettleService.completedSettles(refundSettleIds);
            if (CollectionUtils.isNotEmpty(completedRefundSettles)) {
                // 更新退款结算单状态
                // 推送给到订单中心
                RefundSettle refundSettle = new RefundSettle();
                refundSettle.setSettleStatus(SettleStatusEnum.SUCCESS.getCode());
                refundSettle.setSettleScDate(null == transTime ? LocalDateTime.now().toLocalDate() : transTime.toLocalDate());
                refundSettleService.update(refundSettle, new LambdaQueryWrapper<RefundSettle>().in(RefundSettle::getId, refundSettleIds));
                // 推送给到订单中心
                for (RefundSettle completedRefundSettle : completedRefundSettles) {
                    PayRefundSettleResultMsgBO payRefundSettleResultMsgBO = SettleConvert.INSTANCE.convert2PayRefundSettleResultMsgBO(completedRefundSettle);
                    payRefundSettleResultMsgBO.setSettleScDate(refundSettle.getSettleScDate());
                    payRefundSettleResultMsgBO.setSettleStatus(refundSettle.getSettleStatus());
                    log.info("银商-新购物车退款结算单推送：{}",payRefundSettleResultMsgBO);
                    paymentCenterMsgProducer.refundSettleResult(payRefundSettleResultMsgBO);
                }

            }
        }
        return true;
    }

    @Override
    public List<PaymentFlowDetailSettle> waitSettleAccountsFlows(String payChannel) {
        return this.getBaseMapper().waitSettleAccountsFlows(payChannel);
    }

}
